package com.excilys.formation.project.controllers;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TreeSet;

import javax.validation.Valid;

import org.joda.time.YearMonth;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.excilys.formation.project.controllers.formdata.VirementData;
import com.excilys.formation.project.controllers.util.AuthentifiedClientManager;
import com.excilys.formation.project.entities.Client;
import com.excilys.formation.project.entities.Compte;
import com.excilys.formation.project.entities.Operation;
import com.excilys.formation.project.entities.Transaction;
import com.excilys.formation.project.services.CompteService;
import com.excilys.formation.project.services.TransactionService;

/**
 * Controleur en charge de l'affichage et la gestion des virements.
 * 
 * @author excilys
 * 
 */
@Controller
@RequestMapping(value = "/secure/virement")
public class VirementController {

    /**
     * Une instance de compteService.
     */
    @Autowired
    private CompteService compteService;

    /**
     * Service qui fournira les clients.
     */
    @Autowired
    private TransactionService transactionService;

    /**
     * Gère la récupération du client en session.
     */
    @Autowired
    private AuthentifiedClientManager authentifiedClientManager;

    /**
     * Formulaire de création d'un nouveau virement.
     * 
     * @param model
     *            instance de ModelMap
     * @return chemin vers la jsp
     */
    @RequestMapping(method = RequestMethod.GET)
    public String passerVirement(final ModelMap model) {
        Client connectedClient = authentifiedClientManager.getSessionClient();
        model.addAttribute("client", connectedClient);
        model.addAttribute("comptes", new TreeSet<Compte>(connectedClient.getComptes()));
        model.addAttribute("virement", new VirementData());

        return "secure/virement/new";
    }

    /**
     * Formulaire de création d'un nouveau virement.
     * 
     * @param virementData
     *            données du virement
     * @param result
     *            Résultat de la validation
     * @param model
     *            instance de ModelMap
     * @return chemin vers la jsp
     */
    @RequestMapping(method = RequestMethod.POST)
    public String creerVirement(@ModelAttribute("virement") @Valid VirementData virementData, BindingResult result, final ModelMap model) {
        Client connectedClient = authentifiedClientManager.getSessionClient();

        Compte emetteur = compteService.getCompte(virementData.getEmetteur());
        Compte recepteur = compteService.getCompte(virementData.getRecepteur());
        if (emetteur != null && recepteur != null && !emetteur.getNumero().equals(recepteur.getNumero())
                && emetteur.getClient().getId() == connectedClient.getId() && (virementData.getMontant() < emetteur.getSolde())
                && (!result.hasErrors())) {
            transactionService.creerVirement(emetteur, recepteur, virementData.getMontant());
            return "redirect:virement/listvirement";
        }
        if (virementData.getMontant() > emetteur.getSolde()) {
            model.addAttribute("emetteurError", "Vous n'avez pas les fonds nécessaires pour effecteur ce virement.");
        }

        if (virementData.getEmetteur().equals(virementData.getRecepteur())) {
            model.addAttribute("recepteurError", "Doit être différent du compte emetteur");
        }

        model.addAttribute("error");
        model.addAttribute("virement", virementData);
        model.addAttribute("comptes", new ArrayList<Compte>(connectedClient.getComptes()));
        return "secure/virement/new";
    }

    /**
     * Redirige vers une vue qui contient tous les virements emis par le client connecté.
     * 
     * @param model
     *            Modèle qui sera renvoyé avec la vue.
     * @return La vue et modèle, peuplé des données.
     */
    @RequestMapping(value = "/listvirement")
    public String getListVirements(final ModelMap model) {

        Client connectedClient = authentifiedClientManager.getSessionClient();
        Set<Transaction> virements = transactionService.getVirementsEmisByClientByTimeInterval(
                connectedClient.getId(), YearMonth.now().toInterval());

        DateTimeFormatter dateFormatter = DateTimeFormat.forStyle("LS");
        dateFormatter = dateFormatter.withLocale(Locale.FRANCE);
        List<VirementData> listVirements = new ArrayList<VirementData>();
        model.addAttribute("dateFormatter", dateFormatter);

        for (Transaction transaction : virements) {
            Set<Operation> operations = transaction.getOperations();
            if (operations.size() < 2) {
                continue;
            }
            Operation[] operationsPrincipales = new Operation[2];
            for (Operation operation : operations) {
                if (operation.getMontant() > 0
                        && ((operationsPrincipales[0] != null && operation.getMontant() > operationsPrincipales[0].getMontant())
                        || operationsPrincipales[0] == null)) {
                    operationsPrincipales[0] = operation;
                } else if (operation.getMontant() < 0
                        && ((operationsPrincipales[1] != null && operation.getMontant() < operationsPrincipales[0].getMontant())
                        || operationsPrincipales[1] == null)) {
                    operationsPrincipales[1] = operation;
                }
            }
            if (operationsPrincipales[0] != null & operationsPrincipales[1] != null) {
                VirementData virementData = new VirementData(operationsPrincipales[1].getCompte().getNumero(),
                        operationsPrincipales[0].getCompte().getNumero(),
                        operationsPrincipales[0].getMontant(),
                        transaction.getDate());
                listVirements.add(virementData);
            }
        }
        listVirements.size();
        model.addAttribute(connectedClient);
        model.addAttribute("virements", listVirements);
        return "secure/virement/listview";
    }
}
